#include <boost/foreach.hpp>
#include <boost/algorithm/string.hpp>
#include "Rule.h"
#include "RuleError.h"

//#define DEBUG
#define foreach BOOST_FOREACH

using namespace kc_sf;

// --------------------------------------------------------- //

std::string kc_sf::ActionTypeToString( ActionType action )
{
	return action == Delete ? "usuń wiadomość"
			: action == Quarantine ? "umieść wiadomość w kwarantannie"
			: action == Pass ? "prześlij wiadomość dalej"
			: action == Forward ? "przekieruj wiadomość do innego adresata"
			: "nie znana akcja";
}

// --------------------------------------------------------- //

Rule::Rule( const std::string &expr_str,
	ActionType action,
	int priority )
{
	r_action = action;
	r_priority = priority < 1 ? 1 : priority > 5 ? 5 : priority;
	r_expression = expr_str;
}

// --------------------------------------------------------- //

Rule::Rule( const Rule &rule )
{
	r_action = rule.r_action;
	r_priority = rule.r_priority;
	r_expression = rule.r_expression;
	r_rpn_expression = rule.r_rpn_expression;

	foreach( Logic::Symbol *condition, rule.r_conditions )
	{
		r_conditions.push_back( condition->clone() );
	}
}

// --------------------------------------------------------- //

Rule & Rule::operator =( const Rule &rule )
{
	if( this == &rule )
		return *this;

	for( size_t i = 0; i < r_conditions.size(); ++i )
	{
		delete r_conditions[i];
	}
	r_conditions.clear();

	r_action = rule.r_action;
	r_priority = rule.r_priority;
	r_expression = rule.r_expression;
	r_rpn_expression = rule.r_rpn_expression;

	foreach( Logic::Symbol *condition, rule.r_conditions )
	{
		r_conditions.push_back( condition->clone() );
	}

	return *this;
}

// --------------------------------------------------------- //

Rule::~Rule()
{
	for( size_t i = 0; i < r_conditions.size(); ++i )
	{
		delete r_conditions[i];
	}
}

// --------------------------------------------------------- //

void Rule::addCondition( Logic::Symbol *condition )
{
	r_conditions.push_back( condition->clone() );
}

// --------------------------------------------------------- //

void Rule::buildRpnExpression()
{
    char stack[256];
    int stack_pos = 0;

	std::vector <std::string> words;
	boost::split( words, r_expression, boost::is_any_of( " " ) );
	words.erase( std::remove( words.begin(), words.end(), "" ), words.end() );

	for( size_t i = 0; i < r_expression.size(); ++i )
    {
		char c = r_expression[ i ];

		if( c == ' ' )
			continue;
        if( c >= '0' && c <= '9' )
		{
			r_rpn_expression += c;

			// liczba kilkucyfrowa
			size_t pos = i;
            while( r_expression[ ++pos ] >= '0' && r_expression[ pos ] <= '9' )
				r_rpn_expression += r_expression[ pos ];

			r_rpn_expression += ' ';
			if( pos > i )
				i = pos - 1; // bez ostatniego znaku
		}
        else
		{
            switch( c )
            {
                case '|': ;
					while( stack_pos && stack[ stack_pos - 1 ] != '(' )
					{
						r_rpn_expression += stack[ --stack_pos ];
						r_rpn_expression += ' ';
					}
                    stack[ stack_pos ++ ] = c;
                    break;
                case '&': ;
					while( stack_pos && stack[ stack_pos - 1 ] != '('
						&& stack[ stack_pos - 1 ] != '|' )
					{
						r_rpn_expression += stack[ --stack_pos ];
						r_rpn_expression += ' ';
					}
                    stack[ stack_pos++ ] = c;
                    break;
                case '~':
					while( stack_pos && stack[ stack_pos - 1 ] == '~' )
					{
						r_rpn_expression += stack[ --stack_pos ];
						r_rpn_expression += ' ';
					}
                    stack[ stack_pos++ ] = c;
                    break;
                case '(':
					stack[ stack_pos++ ] = '(';
                    break;
                case ')':
					while( stack[ stack_pos - 1 ] != '(' )
					{
						r_rpn_expression += stack[ --stack_pos ];
						r_rpn_expression += ' ';
					}
                    -- stack_pos;
                    break;
				default:
                    break;
            }
		}
    }

	while( stack_pos )
	{
		r_rpn_expression += stack[ --stack_pos ];
		r_rpn_expression += ' ';
	}
}

// --------------------------------------------------------- //

bool Rule::eval( const Message &msg ) const
{
    bool stack[ 256 ];
	int stack_pos = 0;

	std::vector <std::string> words;
	boost::split( words, r_rpn_expression, boost::is_any_of( " " ) );
	words.erase( std::remove( words.begin(), words.end(), "" ), words.end() );

#ifdef DEBUG
	std::cout << "*** Regula ***" << std::endl;
	std::cout << "Akcja: " << ActionTypeToString( r_action ) << std::endl;
	std::cout << r_expression << std::endl;
#endif // DEBUG

	foreach( std::string word, words )
    {
        if( word[ 0 ] >= '0' && word[ 0 ] <= '9' )
        {
            int index_a = 0,
            	index_b = 0;

			do index_a = 10 * index_a + word[ index_b++ ] - 48;
			while( word[ index_b ] );

			bool eval_result = r_conditions[ index_a - 1 ]->eval( msg );
			stack[ stack_pos++ ] = eval_result;

#ifdef DEBUG
			std::cout << index_a << " => " << eval_result << std::endl;
#endif // DEBUG
        }
        else
        {
            bool right = stack[ --stack_pos ],
            	left = stack[ --stack_pos ],
            	result;

            switch( word[0] )
            {
                case '|': result = left || right; break;
                case '&': result = left && right; break;
                case '~':
					stack[ stack_pos++ ] = left;
					result = !right;
					break;
				default: break;
            }

            stack[ stack_pos++ ] = result;
        }
    }

	if( stack_pos == 0 )
		throw RuleError( "Błąd przetwarzania reguły. Nie ma nic na stosie wynikowym!" );

	bool result = stack[ --stack_pos ];
#ifdef DEBUG
	result
		? std::cout << "Dopasowano" << std::endl
		: std::cout << "Nie dopasowano" << std::endl;
#endif // DEBUG

	return result;
}
